#pragma once

/* wfspan层 */
// #define WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
#include <metalc/block/wfspan_obj.h>
// #define WFSPAN_MBLOCK

OU2INFUN(cls, wfspan_clspol)

#include <metalc/block/cobj.h>
/* 大型 lobj维护 */
OU2INFUN(cobj, wfspan_lobj)

// #define WFSPAN_PRINT(...)	dbg_printf(__VA_ARGS__)
int adebug_f = 0;
#define WFSPAN_PRINT(...)	do {if (atm_ld(&adebug_f)) dbg_printf(__VA_ARGS__); }while(0)

// #define WFSPAN_PRINT(...)	do {if (atm_ld(&adebug_f)) INFO(__VA_ARGS__); }while(0);

SAI void _wfspan_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	WFSPAN_PRINT("_wfspan pinit\n");
}

SAI void _wfspan_tinit(const mlfunc_t * cont, perm_allocf_t *alloc, wfspan_conf_t *conf){
	WFSPAN_PRINT("_wfspan tinit\n");
	mtls_t *tls = mtls_get();
	ASSERT(tls);
	// ASSERT(tls->objs[cont->lidx] == NULL_PTR);
	size_t sz = wfspanobj_size(conf->l1_clsnr, conf->l2_clsnr);
	void *mem = alloc(sz);
	ASSERT(mem);

	/* TODO: 解决重复初始化的问题 */
	if(!tls->objs[cont->lidx]) {
		tls->objs[cont->lidx] = wfspan_init(mem, sz, conf);
	}
	else{
		WFSPAN_PRINT("_wfspan tinit already %p\n", tls->objs[cont->lidx]);
	}
}
#ifdef WFSPAN_MBLOCK
SAI void *wfspan_mblock_oomhandler(wfspan_obj_t *l, mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	/* 申请ret */
	/* 向上申请? */
	req->size = WFSPAN_MBLOCK;
	req->align = WFSPAN_MBLOCK;
	void *mblk_ptr = cont->lalloc(req);
	dbg_printf("[wfspan] oom alloc mblock from upper %p, size %lx\n", mblk_ptr, req->size);
	// WFSPAN_PRINT("[wfspan] oom alloc mblock from upper %p, size %lx\n", mblk_ptr, req->size);

	/* 初始化 */
	if (!mblk_ptr) return ret;
	
	ASSERT(is_ptr_aligned(mblk_ptr, WFSPAN_MBLOCK));
	mblock_t *blk = mblock_init(mblk_ptr, WFSPAN_MBLOCK);
	/* 申请一定成功 */
	/* size alignup了吗? */
	ASSERT(is_aligned(size, conf->mslab_size));
	ret = mblock_alloc(blk, size, WFSPAN_MBLOCK, conf->mslab_size, WFSPAN_MBLOCK/conf->mslab_size);
	
	if (!mblock_is_empty(blk, WFSPAN_MBLOCK/conf->mslab_size)){
		/* 非空 */ 
		/* 插入队列末尾 */
		__dlist_insert(&l->mblk_list, &blk->node, &blk->node, l->mblk_list.prev);
	}

	// if(!ret) WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	ASSERT(ret);
	WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	// WFSPAN_PRINT("[wfspan] mblock alloc %p, size %lx\n", ret, size);
	ASSERT(is_ptr_aligned(ret, conf->mslab_size));
	ASSERT(is_ptr_inbound(ret, blk, (paddr_t)blk + WFSPAN_MBLOCK - conf->mslab_size));
	return ret;
}

/* 顺序应该是 local -> full -> mblock -> mblock */

/* define wfspan */
SAI void *wfspan_try_mblock_alloc(wfspan_obj_t *l, size_t size, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	/* 满足的最大大小 */
	ASSERT (size <= WFSPAN_MBLOCK - conf->mslab_size);
	/* 看block是否满足 */
	if (!list_is_empty(&l->mblk_list)){
		/* 从blk里面申请 */
		mblock_t *blk = list_entry(mblock_t, l->mblk_list.next, node);
		/* size 对齐 */
		ret = mblock_alloc(blk, align_up(size, conf->mslab_size), WFSPAN_MBLOCK, conf->mslab_size, WFSPAN_MBLOCK/conf->mslab_size);
		if (ret){
			if (mblock_is_empty(blk, WFSPAN_MBLOCK/conf->mslab_size)){
				/* 空了, remove*/
				dlist_del(&l->mblk_list, blk->node.next);
			}
				/* 申请成功，返回 */
			ASSERT(is_ptr_aligned(ret, conf->mslab_size));
			ASSERT(is_ptr_inbound(ret, blk, (paddr_t)blk + WFSPAN_MBLOCK - conf->mslab_size));
			return ret;
		}
			/* 申请失败，空间不够，向上再次申请 */
	}
	return ret;
}
#endif

SAI void *wfslab_oomhandler(wfspan_obj_t *l, mreq_t *req, size_t size, const mlfunc_t * cont, wfspan_conf_t *conf){
	void *ret;
	PARAM_USED(l);
#ifdef WFSPAN_MBLOCK
	/* 在本地mblock中申请 */
	if (size <= WFSPAN_MBLOCK - conf->mslab_size){
		ret = wfspan_try_mblock_alloc(l, size, conf);
		if (!ret) ret = wfspan_mblock_oomhandler(l, req, size, cont, conf);
		dbg_printf("[wfmblock] alloc %p, size %lx\n", ret, size);
		return ret;
	}
	
#endif
	/* 大于则直接申请 */

	req->size = size;
	req->align = req->align < conf->mslab_size ? conf->mslab_size : req->align;
	ret = cont->lalloc(req);
	dbg_printf("[wfspan] oom alloc from upper %p, size %lx\n", ret, req->size);


	return ret;
}

SAI bool_t wfslab_try_alloc(mslab_t *cur, void **ptmp, size_t slab_size, hword_t unit_size)
{
	/* 由于wfspan空的都被抛弃，尝试申请的一定只有阻塞和成功获得内存块两种结果 */
	void *tmp_ret = mslab_try_alloc(cur, slab_size, unit_size);
	if (tmp_ret == SLAB_FLIST_BLCOKED){
		return FALSE;
	}
	else{
		*ptmp = tmp_ret;
		return TRUE;
	}
}


SAI void *wfslab_local_full_alloc(wfspan_obj_t *l, wfspan_cls_t *szcls_arr, szcls_t cls, hword_t unit_size, hword_t max_nr, wfspan_conf_t *conf){
	void *ret = NULL_PTR;
	// if (!list_is_empty(&l->full_local_list)){
	if (l->full_nr != 0){
		/* full_list非空 */
		mslab_t *cur_slab = wfspan_pop_full_local_list(l);
		WFSPAN_PRINT("[wfspan] local full list get slab %p\n", cur_slab);
#ifdef WFSPAN_MBLOCK
		void *slab_ptr = mslab_usable_in_mblock(cur_slab, WFSPAN_MBLOCK, conf->mslab_size);

#else
		void *slab_ptr = cur_slab;
#endif

		ret = wfslab_local_init_alloc(l, slab_ptr, conf->mslab_size, szcls_arr, cls, unit_size, max_nr);
	}
	return ret;
}
SAI void *wfslab_acquire_from_others(wfspan_obj_t *l, wfspan_cls_t *szcls_arr, szcls_t cls, size_t unit_size, wfspan_conf_t *conf){
	WFSPAN_PRINT("[wfspan] try stealing cls spmc %u\n", cls);
	void *ret = NULL_PTR;

	/* 先尝试从spmc队列中取，取失败了尝试窃取 */
	mslab_t *cur_slab = NULL_PTR;
	// cur_slab = wfslab_try_b4stealing(l, cls, 1, conf);
	// if (!cur_slab) cur_slab = wfspan_waitfree_acquire(l, cls, 0, conf);

	/* 直接local first窃取 */
	cur_slab = wfspan_waitfree_acquire(l, cls, 1, conf);

	if (LIKELY(cur_slab)){
		WFSPAN_PRINT("[wfspan] steal %p from %u\n", cur_slab, cls);
		/* 窃取成功 */
		/* TODO: 该赋值似乎不需要 */
		/* 尝试在该span分配 */
		ASSERT(cur_slab->allocator == NULL);
		if (wfslab_try_alloc(cur_slab, &ret, conf->mslab_size, unit_size)){	
			/* 分配成功 */
			WFSPAN_PRINT("[wfspan] try alloc %p from stealed %p\n", ret, cur_slab);
			if (!mslab_discard_ifempty(cur_slab, l)){
				/* 不用抛弃 */
				WFSPAN_PRINT("[wfspan] insert stealed slab %p to local cls %u head\n", cur_slab, cls);
				/* 插入头部 */
				wfspan_push_local_list_head(l, szcls_arr, cur_slab, cls);
				ASSERT(cur_slab->cls == cls);
				cur_slab->allocator = l;
			}
		}
		else{
			WFSPAN_PRINT("[wfspan] insert stealed slab %p to spmc cls %u tail\n", cur_slab, cls);
			/* TODO: 塞回去还是插入到队列尾部 */
// #ifdef WFSPAN_BLOCKED_INSERT_TO_SPMC
// 			wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
// #else
// 			cur_slab->allocator = l;
// 			/* 插入头部或者尾部都可以，因为尾部和头部都为空 */
// 			__dlist_insert(szcls_arr[cls].local_list.prev, &cur_slab->node, &cur_slab->node, &szcls_arr[cls].local_list);
// 			ASSERT(cur_slab->cls == cls);
// 			l->slab_nr++;
// #endif
			// ASSERT(cur_slab->consumed_cnt == 0);
			wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
		}
	}
	return ret;
}
SAI void *_wfspan_alloc(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t req_size = req->size;
	/* 自对齐方式 */
	if (UNLIKELY(req->align != 0))
		req_size = align_up(req_size, req->align);
#ifdef WFSPAN_MBLOCK
	/* 允许申请32768 */
	// if (__MLAYER_INVALID_REQSZ(cont->lmaxsize, req_size)) return cont->lalloc(req);
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, req_size)) return cont->lalloc(req);
#else
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, req_size)) return cont->lalloc(req);
#endif
	void *ret;
	void *upper_mem = NULL_PTR;
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);

	if (l2clspol && UNLIKELY(req_size > conf->l1_maxsz)) {
		/* 按l2最小大小对齐 */
		// req_size = align_up(req_size + lobj->mocupysz(), conf->l2_minsz);
		szcls_t cls = l2clspol->sz2cls(req_size) + conf->l1_clsnr;
		size_t aligned_size = l2clspol->cls2sz(cls - conf->l1_clsnr);
		mslab_t *l2block = NULL_PTR;
		
		WFSPAN_PRINT("[wfspan l2] req_size %lx, szcls %u, l2szcls %u, aligend_size %lx\n", req_size, cls, cls - conf->l1_clsnr, aligned_size);
		dbg_printf("[wfspan l2] req_size %lx, szcls %u, l2szcls %u, aligend_size %lx\n", req_size, cls, cls - conf->l1_clsnr, aligned_size);
		ASSERT(aligned_size >= req_size);
		/* 尝试窃取 */
		#ifndef WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
		// if (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size){
		// 	/* 应该可以窃取??是不是meta信息位置有错 */
		// 	l2block = wfslab_try_b4stealing(l, cls, 0, conf);	/* 可以无锁窃取*/
		// 	ASSERT(!l2block || !is_ptr_aligned(l2block, WFSPAN_MBLOCK));
		// }
		// else{
		// 	l2block = wfslab_try_b4stealing(l, cls, 1, conf);	/* 不能无锁窃取，会有修改用户数据的可能性 */
		// 	ASSERT(!l2block ||is_ptr_aligned(l2block, WFSPAN_MBLOCK));
		// }
		if (!l2block) l2block = wfspan_waitfree_acquire(l, cls, 1, conf);	/* 从自己开始满足 */
	#else
		l2block = wfslab_try_b4stealing(l, cls, 1, conf);	 	/* 不安全!! */
		if (!l2block) l2block = wfspan_waitfree_acquire(l, cls, 0, conf);	
	#endif
#ifdef WFSPAN_MBLOCK
		if (l2block && (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size)){
			/*size <= wfspan_block 申请到的是meta节点,需要转换 */
			mslab_t *cur_meta = (mslab_t *)l2block;
			l2block = mslab_usable_in_mblock(cur_meta, WFSPAN_MBLOCK, conf->mslab_size);
			ASSERT(mslab_by_ptr_from_mblock(l2block, WFSPAN_MBLOCK, conf->mslab_size) == cur_meta);
			ASSERT(!is_ptr_aligned(l2block, WFSPAN_MBLOCK));
			// ASSERT((paddr_t)l2block >= ((paddr_t)mblock_by_ptr(l2block, WFSPAN_MBLOCK) + conf->mslab_size));
		}
#endif			
		/* 到这已经是用户可使用部分 */
		/* l2block */
		if (!l2block) {
			/* 向上申请 */
			l2block = (mslab_t *)wfslab_oomhandler(l, req, aligned_size, cont, conf);
			/* TODO: 设置cobj */
			if (!l2block) return NULL_PTR;
#ifdef WFSPAN_MBLOCK

			/* 这里申请出来两种可能，一种是block申请，需要填入meta信息 */
			if (aligned_size <= WFSPAN_MBLOCK - conf->mslab_size){
				ASSERT(!is_ptr_aligned(l2block, WFSPAN_MBLOCK));
				/* 设置meta信息 */
				mslab_t *cur_meta = mslab_by_ptr_from_mblock(l2block, WFSPAN_MBLOCK, conf->mslab_size);
				ASSERT(mslab_usable_in_mblock(cur_meta, WFSPAN_MBLOCK, conf->mslab_size) == l2block);
				cur_meta->cls = cls;
				cur_meta->unit_size = aligned_size;
			}
			else{
				/* 只有大于mblock能分配的大小时才设置 */
				ASSERT (aligned_size > WFSPAN_MBLOCK - conf->mslab_size);
				ASSERT(is_ptr_aligned(l2block, WFSPAN_MBLOCK));
				lobj->set_cobj(l2block, aligned_size, 1);
			}
			
#else
			lobj->set_cobj(l2block, aligned_size, 1);
#endif
		}
		/* 自对齐 */
		ret = l2block;
		req->ptr = l2block;
		req->size = aligned_size;
		req->nr = 1;
		WFSPAN_PRINT("[wfspan l2] "PRIN_RED() "alloc " PRIN_DFT()" %p, size %lx, cls %u\n", ret, aligned_size, cls);
		dbg_printf("[wfspan l2] "PRIN_RED() "alloc " PRIN_DFT()" %p, size %lx, cls %u\n", ret, aligned_size, cls);
		// ASSERT(is_ptr_aligned(l2block, aligned_size));
		return ret;
	}


	mslab_t *cur_slab = NULL_PTR;
	szcls_t cls = clspol->sz2cls(req_size);
	hword_t unit_size = (hword_t)clspol->cls2sz(cls);
	hword_t max_nr = u2mnr(cls);
	WFSPAN_PRINT("[wfspan] try alloc, need size %lx, utsz %x, cls %u\n", req_size, unit_size, cls);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	
	/* 先尝试本地申请 */
	ret = wfspan_local_alloc(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN; 

	/* 优先从本地full_list中满足获取可以降低WCET */
	ret = wfslab_local_full_alloc(l, szclsp, cls, unit_size, max_nr, conf);
	if (LIKELY(ret)) goto FIN;

	/* 本地申请失败，尝试窃取 */
	ret = wfslab_acquire_from_others(l, szclsp, cls, unit_size, conf);
	if (LIKELY(ret)) goto FIN;

	/* 窃取失败，继续向上申请 */
	if (l2clspol){
		/* 向l2窃取一个64KB slab */
		szcls_t spcls = conf->l1_clsnr;
	#ifndef WFSPAN_FAST_BUT_MAYBUGGY	/* 可能修改用户数据 */
		// upper_mem = wfslab_try_b4stealing(l, spcls, 0, conf);
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 1, conf);
	#else
		upper_mem = wfslab_try_b4stealing(l, spcls, 1, conf);		/* 不安全!! */
		if (!upper_mem) upper_mem = wfspan_waitfree_acquire(l, spcls, 0, conf); 
	#endif
		/* TODO: 64Kb slab需要转换*/
#ifdef	WFSPAN_MBLOCK 
		/* 这个是64KB，一定需要转换成指针形势 */
		ASSERT(!upper_mem || !is_ptr_aligned(upper_mem, conf->mslab_size));
		if (upper_mem) upper_mem = mslab_usable_in_mblock((mslab_t *)upper_mem, WFSPAN_MBLOCK, conf->mslab_size);
		ASSERT(!upper_mem || is_ptr_aligned(upper_mem, conf->mslab_size));
#endif
		WFSPAN_PRINT("[wfspan l2] acquire %p, cls %u, size %lx\n", upper_mem, spcls, l2clspol->cls2sz(spcls - conf->l1_clsnr));
		dbg_printf("[wfspan l2] acquire %p, cls %u, size %lx\n", upper_mem, spcls, l2clspol->cls2sz(spcls - conf->l1_clsnr));
	}

	if (!upper_mem) {
		upper_mem = wfslab_oomhandler(l, req, conf->mslab_size, cont, conf);
		ASSERT(!is_ptr_inbound(upper_mem, (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK) + conf->mslab_size - 1));
	}

	if (LIKELY(upper_mem)){
		/* 申请成功 */
		// if (is_ptr_inbound(upper_mem, (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(upper_mem, WFSPAN_MBLOCK) + conf->mslab_size - 1)){
		// 	dbg_printf("upper mem %p, block %p, %lx slab size\n", upper_mem, mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), conf->mslab_size);
		// }

		dbg_printf("upper mem %p, block %p, slab ptr %p %lx slab size, init to %u, unit_size %u, total_nr = %u\n"
		, upper_mem, mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), mslab_by_ptr_from_mblock(upper_mem, WFSPAN_MBLOCK, conf->mslab_size), conf->mslab_size, cls, unit_size, max_nr);
		// ASSERT(!is_ptr_inbound(upper_mem, mblock_by_ptr(upper_mem, WFSPAN_MBLOCK), mblock_by_ptr(upper_mem, WFSPAN_MBLOCK) + conf->mslab_size) - 1);
		ret = wfslab_local_init_alloc(l, upper_mem, conf->mslab_size, szclsp, cls, unit_size, max_nr);
		if (upper_mem == 0x49250000) {
			dbg_printf("0x49250000 req_size %lx, ori %lx, align %lx ret %p\n", req_size, req->size, req->align, ret);
		}
		// ASSERT(!is_ptr_inbound(ret, mblock_by_ptr(ret, WFSPAN_MBLOCK), mblock_by_ptr(ret, WFSPAN_MBLOCK) + conf->mslab_size));
	}
FIN:	
	// if (is_ptr_inbound(ret, (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK) + conf->mslab_size - 1)){
	// 	dbg_printf("ret mem %p, block %p, %lx slab size\n", ret, mblock_by_ptr(ret, WFSPAN_MBLOCK), conf->mslab_size);
	// }
	// ASSERT(!is_ptr_aligned(ret, WFSPAN_MBLOCK));
	// ASSERT(!is_ptr_inbound(ret, (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(ret, WFSPAN_MBLOCK) + conf->mslab_size - 1));
	WFSPAN_PRINT("[wfspan] " PRIN_RED() "alloc " PRIN_DFT() " %p, need size %lx, real size %x, cls %u\n", ret, req_size, unit_size, cls);
	ASSERT(ret);
	ASSERT(unit_size >= req_size);
	req->size = (size_t)unit_size;
	req->ptr = (void *)ret;
	req->nr = 1;
	return ret;
}
SAI dlist_t *wfslab_local_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, size_t *free_slab_nr, wfspan_conf_t *conf){
	PARAM_USED(cls, free_slab_nr);
	/* 本地释放路径 */
	sword_t consumed_cnt = mslab_local_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);
	WFSPAN_PRINT("[wfspan] local free %p in [%p], cur local cnt %ld, max nr %ld\n", free_ptr, s, consumed_cnt, mslab_visible_nr(s));
			
	/* 本地释放应该保留? */
	/* 需要在本地释放路径判断全局大小吗? */
	if ((consumed_cnt >= 0) &&
		/*(mslab_visible_nr(s) + (sword_t)consumed_cnt == slab_max_nr)*/
		__mslab_is_full(s, slab_max_nr)){
		/* 释放节点 */
		wfspan_remove_local_list(l, s);
		/* 优先放到本地full_list的头部 */
		wfspan_push_full_local_list(l, s);
		
		WFSPAN_PRINT("[wfspan] free slab %p to local full list\n", s);
		/* 检查是否出队 */
		/* 是否需要出队? 必须需要吧 */
		if (l->slab_nr >= conf->slab_limits) {
			/* 满的出队 */
			return wfspan_clear_full_local_list(l, free_slab_nr);
		}
		// ASSERT(l->full_nr >= 1 && l->full_nr <= conf->slab_limits && l->full_nr <= l->slab_nr);

	}
	return NULL_PTR;
}
SAI dlist_t *wfslab_remote_free(wfspan_obj_t *l, mslab_t *s, void *free_ptr, szcls_t cls, sword_t slab_max_nr, size_t *free_slab_nr, wfspan_conf_t *conf){
	PARAM_USED(slab_max_nr);
	dlist_t *ret = NULL_PTR;
	/* 使用dlist出队一定要确保node是第一个成员 */
	ASSERT(OFFSETOF(mslab_t, node) == 0);
	wfspan_cls_t *szclsp = wfspanobj_get_clsarr(l);
	// size_t slab_nr = l->slab_nr;

	WFSPAN_PRINT("[wfspan] remote free %p in [%p]\n", free_ptr, s);
	/* 远程释放 */
	sword_t cnt = mslab_remote_free(s, (mlistnode_t *)free_ptr, (mlistnode_t *)free_ptr, 1);

	if (LIKELY(cnt != 0))
		return ret;
	/* 等于0则负责回收 */
	WFSPAN_PRINT("[wfspan] reclaim [%p]\n", s);
	mlistnode_t *reclaimed_list = mlist_hd_exchg(&s->rlist, NULL);

	s->flist = reclaimed_list;
	

	ASSERT(s->cls == cls);
	ASSERT(s->allocator == NULL);
	// TODO: 最近最少使用的出队
	if (l->slab_nr >= conf->slab_limits)
	{
		/* 大于本地限制则出队 */
		/* 优先出队本地已满的 */
		if (l->full_nr != 0) {
			/* 出队已满的最后一个 */
			ret = wfspan_clear_full_local_list(l, free_slab_nr);
		}
		else {
			// if (!list_is_empty(&szclsp[cls].local_list)) {
			// 	/* 没有使用则取最尾部的节点 */
			// 	// 取最尾部
			// 	dlist_t *tail = szclsp[cls].local_list.prev;
			// 	/* 移除节点 */
			// 	dlist_del(tail->prev, &szclsp[cls].local_list);
			// 	mslab_t *rep = list_entry(mslab_t, tail, node);
			// 	ASSERT(rep->cls == cls);
			// 	ASSERT(tail->next == &szclsp[cls].local_list);
			// 	slab_nr -= 1;
			// 	/* 设置为无主 */
			// 	// atm_st(&rep->allocator, NULL_PTR);
			// 	rep->allocator = NULL_PTR;
			// 	WFSPAN_PRINT("[wfspan] insert recently unused [%p] to spmc list\n", rep);
			// 	wfspan_researved_mslabn(l, cls, rep, rep);
			// }
			// else {
			// 	WFSPAN_PRINT("[wfspan] insert reclaim [%p] to spmc list\n", s);
			// 	/* 将回收的span出队 */
			// 	wfspan_researved_mslabn(l, cls, s, s);
			// 	return ret;
			// }
			ASSERT(!list_is_empty(&l->non_full_rcu_list));
			/* 出队 */
			mslab_t *rep = wfspan_pop_recently_unused(l);
			rep->allocator = NULL_PTR;
			szcls_t repcls = rep->cls;
			INFO("[wfspan] insert recently unused [%p] to spmc list\n", rep);
			// wfspan_researved_mslabn(l, repcls, rep, rep);
			/* 使用该节点清理队列 */
			rep->node.next = NULL_PTR;
			/* TODO: 增加一个限制n，以限制回收步骤 */
			ret = wfspan_clean_spmclists(l, repcls, (spmcnode_t *)rep, free_slab_nr, conf);
			/* 只瞥4个 */
			// ret = wfspan_clean_spmclists_limit(l, repcls, (spmcnode_t *)rep, free_slab_nr, 4, conf);
		
		}
	}

	WFSPAN_PRINT("[wfspan] reclaim [%p] to local list\n", s);
	/* 设置所有者 */
	s->allocator = l;

	/* 插入到尾部等待复用 */
	wfspan_push_local_list_tail(l, szclsp, s, cls);
	return ret;
}
/* TODO: 查找Allocator的时候也可以通过本地索引判断？这样也能减少Cache行的竞争 */


static inline bool_t wfslab_oofhandler(mreq_t *req, dlist_t *s, size_t nr, const mlfunc_t * cont, wfspan_conf_t *conf)
{
	ASSERT(is_ptr_aligned(s, conf->mslab_size));
	WFSPAN_PRINT("[wfspan] oof free slab %p, ptr %p, size %lx\n", s, req->ptr, conf->mslab_size);
	req->ptr = s;
	req->sptr = s->prev;
	// s->prev->next = NULL_PTR;
	req->size = conf->mslab_size;
	req->nr = nr;
	return cont->lfree(req);
}
/* 优化方案1， max_nr和owner_tid编码 */
SAI bool_t _wfspan_free(mreq_t *req, const mlfunc_t * cont, cls2maxnr * const u2mnr, wfspan_conf_t *conf, \
INFUN(wfspan_clspol, clspol), INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	size_t get_size = req->size;
#ifdef WFSPAN_MBLOCK
	/* 允许申请32768 */
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, get_size)) return cont->lfree(req);
	// if (__MLAYER_INVALID_REQSZ(cont->lmaxsize, get_size)) return cont->lfree(req);
#else
	if (__MLAYER_INVALID_REQSZ(conf->max_sz, get_size)) return cont->lfree(req);
#endif
	mtls_t *tls = mtls_by_req(req);
	wfspan_obj_t *l = (wfspan_obj_t *)MLAYER_GET_OBJ(tls, cont);
	ASSERT(l);
	
	/* 启用l2时的释放路径 > 32768 */
	if (l2clspol && UNLIKELY(get_size > conf->l1_maxsz)) {
		/* 直接插入到spmc链表中 */
	
		szcls_t cls = l2clspol->sz2cls(get_size) + conf->l1_clsnr;
		mslab_t *cur_slab;
		WFSPAN_PRINT("[wfspan l2] try free %p, size %lx, cls %u\n", req->ptr, req->size, cls);
		dbg_printf("[wfspan l2] try free %p, size %lx, cls %u\n", req->ptr, req->size, cls);
#ifdef	WFSPAN_MBLOCK 
		/* 需要转换 */
		if (!is_ptr_aligned(req->ptr, WFSPAN_MBLOCK)){
			ASSERT(get_size <= WFSPAN_MBLOCK - conf->mslab_size);
			/* 大小小于，填入cur_slab */
			cur_slab = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
			ASSERT(mslab_usable_in_mblock(cur_slab, WFSPAN_MBLOCK, conf->mslab_size) == req->ptr);
		}
		else
#endif
			cur_slab = (mslab_t *)lobj->uptr2ptr(req->ptr);
		WFSPAN_PRINT("[wfspan l2]" PRIN_YELLOW() " free" PRIN_DFT()" %p slab %p, size %lx, cls %u\n", req->ptr, cur_slab, get_size, cls);
		dbg_printf("[wfspan l2]" PRIN_YELLOW() " free" PRIN_DFT()" %p slab %p, size %lx, cls %u\n", req->ptr, cur_slab, get_size, cls);
		/* 不做检查 */
		/* > WFSPAN MBLOCK的大小需要以头部入队，否则以block中的Meta指针入队 */
		__wfspan_researved_mslabn(l, cls, cur_slab, cur_slab);
		return TRUE;
	}
	
	/* wfslab释放 */
#ifdef WFSPAN_MBLOCK
	mslab_t *s = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
#else
	mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
#endif
	wfspan_obj_t *r = (wfspan_obj_t *)s->allocator;
	// 
	PARAM_USED(clspol);
	szcls_t cls = req->cls;
	hword_t max_nr = u2mnr(cls);
	WFSPAN_PRINT("[wfspan] try free %p, size %lx, cls %u, slab %p\n", req->ptr, req->size, cls, s);
	/* cid_t tid = tls->apdstls.tWFSPAN_PRINT.tid; */
	ASSERT(req->nr == 1);
	
	ASSERT(s->cls == cls);

	/* 释放 */
	dlist_t *ret;
	size_t free_slab_nr = 0;
	if (r == l){
		/* 本地释放 */
		ret = wfslab_local_free(l, s, req->ptr, cls, (sword_t)max_nr, &free_slab_nr, conf);
	}else{
		ret = wfslab_remote_free(l, s, req->ptr, cls, (sword_t)max_nr, &free_slab_nr, conf);
	}
	/* 释放后需要向上层释放 */
	if (UNLIKELY(ret)) {
		ASSERT(free_slab_nr != 0);

		if (l2clspol) {
			/* slab页向自己释放 */
			szcls_t pcls = conf->l1_clsnr;
			WFSPAN_PRINT("[wfspan l2] "PRIN_YELLOW() "free slab" PRIN_DFT()" %p, size %lx, cls %u\n", ret, get_size, cls);
			dbg_printf("[wfspan l2] "PRIN_YELLOW() "free slab" PRIN_DFT()" %p, size %lx, cls %u\n", ret, get_size, cls);
			/* 不做检查 */
			__wfspan_researved_mslabn(l, pcls, (mslab_t *)ret, (mslab_t *)ret->prev);
			return TRUE;
		}
		
#ifdef WFSPAN_MBLOCK
		/* 要转换 */
		dlist_t new_list;
		dlist_init(&new_list);
		dlist_t *cur = ret;
		dlist_t *tail = ret->prev;
		dlist_t *next;
		tail->next = NULL_PTR;
		do {
			next = cur->next;
			dlist_t *real_ptr = mslab_usable_in_mblock((mslab_t *)cur, WFSPAN_MBLOCK, conf->mslab_size);
			dlist_add_tail(&new_list, real_ptr);
			cur = next;
		} while(cur);
		ret = new_list.next;
		ret->prev = new_list.prev;
#endif
		return wfslab_oofhandler(req, ret, free_slab_nr, cont, conf);
	}
	WFSPAN_PRINT("[wfspan] "PRIN_YELLOW() "free" PRIN_DFT() "%p, size %lx, cls %u, slab %p\n", req->ptr, req->size, cls, s);
	return TRUE;
}

/* 窃取(cache不友好但有助于减少内存占用) vs 帮助入队(cache友好但是会导致问题) */
/* TODO优化点 */
/* TODO:回收span后优先交还给拥有线程，只有拥有线程竞争激烈时才会自己占有 */
/* 只有free过的线程才会被加入窃取队列?否则一定为空 */
/* 定期清理链表?(谁来清理?如何保证内存安全?) */

SAI size_t _wfspan_sizeof(mreq_t *req, const mlfunc_t * cont, wfspan_conf_t *conf, INFUN(wfspan_clspol, l2clspol), INFUN(wfspan_lobj, lobj)){
	ASSERT(req->ptr);
#ifdef WFSPAN_MBLOCK
	const size_t align_size = WFSPAN_MBLOCK;
#else
	const size_t align_size = conf->mslab_size;
#endif
	/* 如果不按wfspan_block_size对齐就是本层管理的  */
	if (!is_ptr_aligned(req->ptr, align_size)){
		/* 对齐后也有两种情况 */
		/* 一种对齐 */
		if (is_ptr_inbound(req->ptr, (paddr_t)mblock_by_ptr(req->ptr, WFSPAN_MBLOCK), (paddr_t)mblock_by_ptr(req->ptr, WFSPAN_MBLOCK) + conf->mslab_size - 1)){
			dbg_printf("sizeof mem %p, block %p, %lx slab size\n", req->ptr, mblock_by_ptr(req->ptr, WFSPAN_MBLOCK), conf->mslab_size);
			// return 0;
		}
#ifdef WFSPAN_MBLOCK
		mslab_t *s = mslab_by_ptr_from_mblock(req->ptr, WFSPAN_MBLOCK, conf->mslab_size);
		// dbg_printf("[wfspan mblock] ptr %p, mslab %p\n", req->ptr, s);
#else
		mslab_t *s = mslab_byptr(req->ptr, conf->mslab_size);
#endif
		word_t cls_size = s->cls_size;
		szcls_t cls = word_high(cls_size);
		hword_t unit_size = word_low(cls_size);
		WFSPAN_PRINT("[wfspan] sizeof %p, sz %x, cls %u\n", req->ptr, unit_size, cls);
		req->size = unit_size;
		req->cls = cls;
		/* WFSPAN_PREFETCH(req->ptr); */ /* 提前预取，因为插入队列一定需要访问 */
		return req->size;
		/* 对齐且存在小的情况? */
	} else if (l2clspol) {
		/* 对齐且存在大的 */
		void *ptr = lobj->uptr2ptr(req->ptr);
		size_t size = lobj->ptr_getsize(ptr);
		req->ptr = ptr;
		req->size = size;
		WFSPAN_PRINT("[wfspan l2] sizeof %p, sz %lx\n", req->ptr, size);
		return size - lobj->mocupysz();
	}
	return cont->lsizeof(req);
}

/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * slab_size - slab大小
 * slab_limit - 线程持有的slab个数限制，推荐为41
 * p_frac - travel的分数，推荐为0，越大内存占用越高，性能越好
 * help_quota - 单次帮助数量，推荐为1
 * cls_comp - szcls策略
 * u2mfunc - unit_size到max_nr的映射函数
 * perm_alloc - 永久对象分配函数
*/
#define DEF_WFSLAB_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 1)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.max_help_quota = help_quota, .max_sz = _maxsz_##cls_comp, .l1_minsz = BIT(min_bit), .l1_maxsz = _maxsz_##cls_comp, \
.l2_minsz = 0, .l2_maxsz = 0, .l1_clsnr = _clsnr_##cls_comp + 1, .l2_clsnr = 0 }; \
SAI void name##_pinit(const mlfunc_t * cont){ return _wfspan_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfspan_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), NULL_PTR, NULL_PTR);\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_sizeof(req, cont, &name##_conf, NULL_PTR, NULL_PTR); }


/* 增加2级 */
#define DEF_WFSPAN_MLAYER(name, min_bit, max_bit, slab_size, slab_limit, p_frac, help_quota, cls_comp, u2mfunc, perm_alloc, l2_max_bit, os_alloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit + l2_max_bit), 1)\
DEF_CLS_POW2B(name##_2b, max_bit + 1, HDEF_PTR_BITS)\
static cobj_ord_t *pext_##name;\
COBJ_EXT(ext_##name, pext_##name, slab_size)\
static const wfspan_conf_t name##_conf = { .mslab_size = slab_size, .slab_limits = slab_limit, .travel_frac = p_frac, \
.max_help_quota = help_quota, .max_sz = _maxsz_##name##_2b, .l1_minsz = BIT(min_bit), .l1_maxsz = BIT(15), \
.l2_minsz = BIT(max_bit + 1), .l2_maxsz = _maxsz_##name##_2b, .l1_clsnr = _clsnr_##cls_comp + 1, .l2_clsnr = _clsnr_##name##_2b }; \
SAI void name##_pinit(const mlfunc_t * cont){ if(!pext_##name){ \
	pext_##name = os_alloc(BIT(HDEF_PTR_BITS)/slab_size);\
	}\
	return _wfspan_pinit(cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _wfspan_tinit(cont, perm_alloc, &name##_conf); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_alloc(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_free(req, cont, u2mfunc, &name##_conf, OUFUN_OF(cls_comp), OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _wfspan_sizeof(req, cont, &name##_conf, OUFUN_OF(name##_2b), OUFUN_OF(ext_##name)); }
